package caches

import (
	"gitee.com/captials-team/ubdframe/src/common/utils"
	"gitee.com/captials-team/ubdframe/src/domain/interfaces"
	"gitee.com/captials-team/ubdframe/src/pkg/codec"
	"gitee.com/captials-team/ubdframe/src/pkg/logs"
	"reflect"
	"time"
)

func NewCacheFacade[KeyT int | int64 | string | []string | interface{}, ValueT interface{}](cache ItfCache) *CacheFacade[KeyT, ValueT] {
	return &CacheFacade[KeyT, ValueT]{
		cache: cache,
		cdc:   new(codec.JsonCodec),
	}
}

// CacheFacade cache门面
// valueT返回使用指针的原因：若缓存值不存在则会返回nil
type CacheFacade[KeyT int | int64 | string | interface{}, ValueT interface{}] struct {
	cache ItfCache
	cdc   interfaces.Codec //编码器

	//目前暂不支持，支持需要parseKeyFunc的
	keyFunc func(KeyT) string //自定义key转换的方法

	valueFunc func(KeyT) ValueT //value获取的方法
	keyPrefix string            //Key前缀
}

// Get 直接获取缓存
func (ca *CacheFacade[KeyT, T]) Get(k KeyT) (T, error) {
	var v T
	relKey := ca.key(k)

	s, exist, err := ca.cache.Get(relKey)
	if err != nil {
		return v, err
	}
	if !exist {
		return v, nil
	}
	if string(s) == "" {
		return v, nil
	}

	return ca.parseValue(s)
}

// GetOrLoad 获取缓存，当不存在时进行加载
func (ca *CacheFacade[KeyT, T]) GetOrLoad(k KeyT) (T, error) {
	relKey := ca.key(k)
	var v T

	s, exist, err := ca.cache.Get(relKey)
	if err != nil {
		return v, err
	}
	if !exist {
		return ca.Load(k)
	}
	if string(s) == "" {
		return v, nil
	}

	return ca.parseValue(s)
}

// Load 重新加载入缓存
func (ca *CacheFacade[KeyT, T]) parseValue(s string) (T, error) {
	var d T
	var err error

	tyRef := reflect.TypeOf(d)
	//针对指针进行特殊处理
	if tyRef.Kind() == reflect.Pointer {
		d = reflect.New(tyRef.Elem()).Interface().(T)
		err = ca.cdc.Unmarshal([]byte(s), d)
	} else {
		err = ca.cdc.Unmarshal([]byte(s), &d)
	}

	return d, err
}

// Load 重新加载入缓存
func (ca *CacheFacade[KeyT, T]) Load(k KeyT) (T, error) {
	value := ca.value(k)
	err := ca.SetDefault(k, value)
	return value, err
}

// Set 手动设置值
func (ca *CacheFacade[KeyT, T]) Set(k KeyT, v T, expiration time.Duration) error {
	relKey := ca.key(k)
	if utils.IsNil(v) {
		return ca.cache.SetExpire(relKey, "", expiration)
	}
	value, err := ca.cdc.Marshal(v)
	if err != nil {
		return err
	}
	return ca.cache.SetExpire(relKey, string(value), expiration)
}

func (ca *CacheFacade[KeyT, T]) SetDefault(k KeyT, v T) error {
	relKey := ca.key(k)

	logs.Out.Info("RelKey= %s", relKey)

	if utils.IsNil(v) {
		return ca.cache.Set(relKey, "")
	}
	value, err := ca.cdc.Marshal(v)
	if err != nil {
		return err
	}
	return ca.cache.Set(relKey, string(value))
}

func (ca *CacheFacade[KeyT, T]) Delete(k KeyT) error {
	relKey := ca.key(k)
	return ca.cache.Delete(relKey)
}

func (ca *CacheFacade[KeyT, T]) Keys() ([]KeyT, error) {
	keys, err := ca.cache.Keys()
	if err != nil {
		return nil, err
	}

	var ts []KeyT
	for _, key := range keys {
		ts = append(ts, ParseGenericKey[KeyT](key))
	}

	return ts, nil
}

func (ca *CacheFacade[KeyT, T]) key(key KeyT) string {
	if ca.keyFunc == nil {
		return GenericKey(key)
	}
	return ca.keyFunc(key)
}

func (ca *CacheFacade[KeyT, T]) value(key KeyT) T {
	var v T
	if ca.valueFunc == nil {
		return v
	}
	return ca.valueFunc(key)
}

func (ca *CacheFacade[KeyT, T]) WithFunc(keyFunc func(KeyT) string, valueFunc func(KeyT) T) {
	ca.keyFunc = keyFunc
	ca.valueFunc = valueFunc
}

func (ca *CacheFacade[KeyT, T]) WithKeyPrefix(pre string) *CacheFacade[KeyT, T] {
	ca.cache.SetKeyPrefix(pre)
	return ca
}

func (ca *CacheFacade[KeyT, T]) WithValueFunc(valueFunc func(KeyT) T) *CacheFacade[KeyT, T] {
	ca.valueFunc = valueFunc
	return ca
}

func (ca *CacheFacade[KeyT, T]) WithCodec(cdc interfaces.Codec) *CacheFacade[KeyT, T] {
	ca.cdc = cdc
	return ca
}
